Diferença entre: char *argv[] e char argc[]

1. Diferença entre: char *argv[] e char argc[]

Beatriz da Silveira
beatrizs

(usa Outra)

Enviado em 15/08/2012 - 14:21h

Quero saber a diferença entre: char *argv[] e char argc[]


  


2. Re: Diferença entre: char *argv[] e char argc[]

cr0n
_di0

(usa FreeBSD)

Enviado em 15/08/2012 - 14:40h

char argc[] = vetor de caracteres

char *argv[] = array de string (string, por sua vez, vetor de caractere)

Se estiver referenciando ao parâmetros que pode ser passados pela função main();

argc é do tipo int, que contabiliza o número de parâmetros que foram passados via linha
de comando quando o programa iniciou e *argv[] diz que parâmetros foram esses, a grosso modo.


3. Re: Diferença entre: char *argv[] e char argc[]

Alexandre (azk4n)
azk

(usa Arch Linux)

Enviado em 15/08/2012 - 14:42h

Aqui explica:
http://mtm.ufsc.br/~azeredo/cursoC/aulas/c790.html


4. Re: Diferença entre: char *argv[] e char argc[]

Beatriz da Silveira
beatrizs

(usa Outra)

Enviado em 15/08/2012 - 16:13h

eu não entendi pra que serve o *, afinal, quando declaro um char o nome da variável já não é o próprio ponteiro?


5. Re: Diferença entre: char *argv[] e char argc[]

Perfil removido
removido

(usa Nenhuma)

Enviado em 15/08/2012 - 16:52h


beatrizs escreveu:

Quero saber a diferença entre: char *argv[] e char argc[]


beatrizs escreveu:

eu não entendi pra que serve o *, afinal, quando declaro um char o nome da variável já não é o próprio ponteiro?


Seja um programa chamado "prog1" que foi executado da shell com os seguintes parâmetros:

$ ./prog1 abc de2 345 7

A função main() deste programa foi compilada com:

main(int argc, char *argv[]) 


argc é um número que indica a quantidade de parâmetros que foram passados pelo prompt.

No caso argc = 4.

argv[] é um vetor de strings com esses argumentos. No caso:

argv[0]="prog1"
argv[1]="abc"
argv[2]="de2"
argv[3]="345"
argv[4]="7"

"345" é uma string que precisa ser convertida para número, caso queira-se fazer algum cálculo com ela.

"7" não é um dígito e nem um caracter. É uma string também. Em notação de vetor seria:

argv[4]={'7','{TTEXTO}'}

"char argv[]" é um array de caracteres. "char *argv[]" é um array de ponteiros de caracteres. Se ao invés de argv fosse

char *string0="prog1"
char *string1="abc"
char *string2="de2"
char *string3="345"
char *string4="7"

que também pode ser escrito como:

char string0[]="prog1"
char string1[]="abc"
char string2[]="de2"
char string3[]="345"
char string4[]="7"

onde os colchetes estão vazios porque não se indica ou sabe-se o tamanho da string. Então ficaria

argv = { *string0, *string1, *string2, *string3, *string4 }

ou

argv = { string0[], string1[], string2[], string3[], string4[] }

No caso ele indica em que parte da memória estão os parãmetros. Então argv é um ponteiro. As sequências string0[], string1[]... também são ponteiros para caracteres.

Se houvesse apenas uma string no parâmetro, no caso *string0 ou string[] porque não é dito o tamanho, argv apontaria apenas para ela.

*argv -> *string0

É um ponteiro para um ponteiro, que pode ser escrito como **argv, notação antiga usada dentro de main().

Mas como arg é um vetor, então

*argv -> { *string0, *string1, *string2, *string3, *string4 }

E por não se expressar também o tamanho desse vetor, escreve-se

*argv[]

ou

**argv por ser um ponteiro que aponta para ponteiros.

Perceba que o único modo de conseguir usar os ponteiros ou parâmetros do vetor argv[] é com o auxílio de argc, informado justamente para isto.

argv[] é um vetor que não diz quantos parâmetros possui ou receberá. Indica o endereço do primeiro elemento.

:\






6. Re: Diferença entre: char *argv[] e char argc[]

Paulo
paulo1205

(usa Ubuntu)

Enviado em 17/08/2012 - 19:43h

Listeiro 037 escreveu:

"7" não é um dígito e nem um caracter. É uma string também. Em notação de vetor seria:

argv[4]={'7','{TEXTO}'}


Entendo que você quis dizer argv[4]={'7', 0};, e que foi o programa do fórum que trocou o sinal de contrabarra seguido de "0" por essa string "{TEXTO}".

"char argv[]" é um array de caracteres. "char *argv[]" é um array de ponteiros de caracteres. Se ao invés de argv fosse

char *string0="prog1"
char *string1="abc"
char *string2="de2"
char *string3="345"
char *string4="7"

que também pode ser escrito como:

char string0[]="prog1"
char string1[]="abc"
char string2[]="de2"
char string3[]="345"
char string4[]="7"

onde os colchetes estão vazios porque não se indica ou sabe-se o tamanho da string. Então ficaria

argv = { *string0, *string1, *string2, *string3, *string4 }

ou

argv = { string0[], string1[], string2[], string3[], string4[] }

No caso ele indica em que parte da memória estão os parãmetros. Então argv é um ponteiro. As sequências string0[], string1[]... também são ponteiros para caracteres.


Seria bom destacar que nada do que vai acima é C (i.e. não compilaria), mas uma construção parecida com fins meramente didáticos. Mas, nesse caso, acho que seria mais apropriado dizer que "os colchetes estão vazios para que o compilador calcule o tamanho da string e crie arrays com os tamanhos mínimos necessários para armazenar os dados indicados" (admitindo-se, por exemplo, uma declaração como char string2[]="de2";, é óbvio que o tamanho do array é 4, e não desconhecido).

O fato é que é uma coisa muito infeliz para iniciantes a forma como C trabalha com strings. E mesmo outros tipos de arrays estão longe de ser intuitivos para quem não tem ainda alguma familiaridade com programação.

Se houvesse apenas uma string no parâmetro, no caso *string0 ou string[] porque não é dito o tamanho, argv apontaria apenas para ela.

*argv -> *string0

É um ponteiro para um ponteiro, que pode ser escrito como **argv, notação antiga usada dentro de main().

Mas como arg é um vetor, então

*argv -> { *string0, *string1, *string2, *string3, *string4 }

E por não se expressar também o tamanho desse vetor, escreve-se

*argv[]

ou

**argv por ser um ponteiro que aponta para ponteiros.


Não consegui ver a lógica desse salto lógico, mas tudo bem.

Além do mais, eu penso que "notação antiga", se há, é a que usa colchetes, pois ela era a notação original para designar ponteiros em B, a linguagem que antecedeu o C. O asterisco veio anos depois, já com o C.

Sobre essa notação, é interessante notar o seguinte: quando os colchetes vazios são usados numa declaração de argumento de função, eles são um sinônimo perfeito da declaração feita apenas com asterisco (i.e. int main(int argc, char **argv) é exatamente a mesma coisa que int main(int argc, char *argv[]), em que argv é literalmente uma "variável do tipo ponteiro para dados do tipo ponteiro para dados do tipo char", deixando bem clara a herança da notação de B). Há quem argumente que se deva preferir "[]" quando se for usar o endereço associado ao ponteiro para acesso a vários elementos em adjacentes na memória, e asteriscos quando o uso principal for a referência a um único elemento, residente apenas no endereço indicado pelo ponteiro. Entendo os motivos, mas discordo da abordagem.

No entanto, quando não se tratar da declaração de um argumento de função, o que se terá é um array, não uma variável do tipo ponteiro (faço essa distinção porque o nome de um array, ainda que, quando usado, tenha o valor de um endereço de memória, não é uma variável porque não é um lvalue). E o cálculo do tamanho mímino necessário sempre ocorre: por exemplo, se se delcarar um array de caracteres sem se especificar seu tamanho nem se lhe definir um valor inicial (como em char vc[];), o compilador gerará um array de tamanho 1, suficiente apenas para armazenar o byte nulo.

Perceba que o único modo de conseguir usar os ponteiros ou parâmetros do vetor argv[] é com o auxílio de argc, informado justamente para isto.


Talvez isso seja verdade em geral, mas acho interessante observar que não é verdade nos UNIXes em geral, e particularmente no Linux.

Um novo programa é sempre chamado pela system call execve(), que recebe, entre seus argumentos, um argv, do tipo char **, mas nada parecido com um inteiro argc. Essa função conta os argumentos de modo parecido com a forma como strings identificam o final da cadeia de caracteres: usando um marcador com valor nulo como último elemento do array. Esse marcador nulo, que deve estar presente quando execve() é invoca é passado por ela ao novo programa qur ela abre. Desse modo, é seguro fazer no Linux algo como vai abaixo.

int main(int argc, char **argv){
assert(argv[argc]==NULL);
/* ... */
}



7. Re: Diferença entre: char *argv[] e char argc[]

Perfil removido
removido

(usa Nenhuma)

Enviado em 17/08/2012 - 23:51h

Vamos lá:

paulo1205 escreveu:

Listeiro 037 escreveu:

"7" não é um dígito e nem um caracter. É uma string também. Em notação de vetor seria:

argv[4]={'7','{TEXTO}'}


Entendo que você quis dizer argv[4]={'7', 0};, e que foi o programa do fórum que trocou o sinal de contrabarra seguido de "0" por essa string "{TEXTO}".



Isso seria da construção do fórum. A tal ' \ 0 ' se for escrita sem os espaços como deve-se escrever em C apareceria como '{TEXTO}'.

paulo1205 escreveu:

Listeiro 037 escreveu:

"char argv[]" é um array de caracteres. "char *argv[]" é um array de ponteiros de caracteres. Se ao invés de argv fosse

char *string0="prog1"
char *string1="abc"
char *string2="de2"
char *string3="345"
char *string4="7"

que também pode ser escrito como:

char string0[]="prog1"
char string1[]="abc"
char string2[]="de2"
char string3[]="345"
char string4[]="7"

onde os colchetes estão vazios porque não se indica ou sabe-se o tamanho da string. Então ficaria

argv = { *string0, *string1, *string2, *string3, *string4 }

ou

argv = { string0[], string1[], string2[], string3[], string4[] }

No caso ele indica em que parte da memória estão os parãmetros. Então argv é um ponteiro. As sequências string0[], string1[]... também são ponteiros para caracteres.


Seria bom destacar que nada do que vai acima é C (i.e. não compilaria), mas uma construção parecida com fins meramente didáticos. Mas, nesse caso, acho que seria mais apropriado dizer que "os colchetes estão vazios para que o compilador calcule o tamanho da string e crie arrays com os tamanhos mínimos necessários para armazenar os dados indicados" (admitindo-se, por exemplo, uma declaração como char string2[]="de2";, é óbvio que o tamanho do array é 4, e não desconhecido).

[/quote]

Escrevi o texto de uma vez só e evitei de ficar reformatando por muito tempo. Estava esperando a pessoa que criou o tópico dizer que não entendeu prá tentar remendar. :\

Nem pensei se compilaria ou não, mas agora fiquei interessado em testar algumas sintaxes. Exceto por esse número à frente da variável, que não é um índice. Realmente quem desconhece pode pensar isto. Não sei se isso seria pior do que criar um nome aleatório para representar cada elemento no vetor de parâmetros. Lembrarei-me disto algum dia no futuro.

Não ousaria dizer exatamente a notação que 'tentei' fazer, mas posso dizer que prefiro isso do que portugol. Não me preocupei com o rigor do formalismo, mas com uma outra abordagem.

Quando você escreve char a[] = "abcd" é óbvio que você vê quatro caracteres após o sinal de igual. Mas e o tal barra-zero? É quatro ou é cinco? E se a string tivesse 1337 caracteres? E se fossem 1338? Nesse sentido desconhece-se quantos caracteres há na string. Não precisa contar, saber, pensar em quantos há exatamente. Inclusive char a[] = "abcd" é char *a = "abcd"

No vetor de parâmetros a situação é semelhante. Quem "sabe" quantos parâmetros há é o outro parâmetro.

Eu nem quis dizer que main() possui três parâmetros prá não complicar mais a coisa. ¬¬

paulo1205 escreveu:

O fato é que é uma coisa muito infeliz para iniciantes a forma como C trabalha com strings. E mesmo outros tipos de arrays estão longe de ser intuitivos para quem não tem ainda alguma familiaridade com programação.



Mesmo assim alguns compreenderam e não tenho a menor ideia de como o fizeram.

paulo1205 escreveu:

Listeiro 037 escreveu:

Se houvesse apenas uma string no parâmetro, no caso *string0 ou string[] porque não é dito o tamanho, argv apontaria apenas para ela.

*argv -> *string0

É um ponteiro para um ponteiro, que pode ser escrito como **argv, notação antiga usada dentro de main().

Mas como arg é um vetor, então

*argv -> { *string0, *string1, *string2, *string3, *string4 }

E por não se expressar também o tamanho desse vetor, escreve-se

*argv[]

ou

**argv por ser um ponteiro que aponta para ponteiros.


Não consegui ver a lógica desse salto lógico, mas tudo bem.


Garanto que não foi o único. ¬¬

paulo1205 escreveu:

Além do mais, eu penso que "notação antiga", se há, é a que usa colchetes, pois ela era a notação original para designar ponteiros em B, a linguagem que antecedeu o C. O asterisco veio anos depois, já com o C.


Escrevi essa referência pela memória. Eu li uma recomendação de que há uma notação a ser evitada pelo desuso, mas se eu encontrar onde eu li, creio que não será importante. Quanto ao B, vou pensar no caso.

paulo1205 escreveu:

Sobre essa notação, é interessante notar o seguinte: quando os colchetes vazios são usados numa declaração de argumento de função, eles são um sinônimo perfeito da declaração feita apenas com asterisco (i.e. int main(int argc, char **argv) é exatamente a mesma coisa que int main(int argc, char *argv[]), em que argv é literalmente uma "variável do tipo ponteiro para dados do tipo ponteiro para dados do tipo char", deixando bem clara a herança da notação de B). Há quem argumente que se deva preferir "[]" quando se for usar o endereço associado ao ponteiro para acesso a vários elementos em adjacentes na memória, e asteriscos quando o uso principal for a referência a um único elemento, residente apenas no endereço indicado pelo ponteiro. Entendo os motivos, mas discordo da abordagem.


Tudo bem. Eu discordo de comparar uma linguagem de 40 anos atrás com uma de 50 anos atrás, um ano a mais ou a menos, não importa. A coisa já está muito complicada em facilitar alguma explicação com este nó que eu fiz. Eu me preocupava mais em tentar diagramar numa linguagem acessível. Mas se quer saber, conseguiu me fazer dar a mínima.

Caracteres de texto são muito menos flexíveis para diagramar que papel. Eu escrevi como me veio à mente no momento e em poucos minutos e pareceu-me claro. Não é unanimidade. Agora nunca será mesmo.

paulo1205 escreveu:

No entanto, quando não se tratar da declaração de um argumento de função, o que se terá é um array, não uma variável do tipo ponteiro (faço essa distinção porque o nome de um array, ainda que, quando usado, tenha o valor de um endereço de memória, não é uma variável porque não é um lvalue). E o cálculo do tamanho mímino necessário sempre ocorre: por exemplo, se se delcarar um array de caracteres sem se especificar seu tamanho nem se lhe definir um valor inicial (como em char vc[];), o compilador gerará um array de tamanho 1, suficiente apenas para armazenar o byte nulo.


A situação de 'saber' o valor implica em contar o número de caracteres. O compilador sabe porque ele contou. Eu posso não ter contado. Esse caso é o 'não se sabe'. "O compilador que se vire".

Entendi o caso do lvalue, mas o exemplo não teve tempo de ser tão aprofundado. Não houve o rigor do formalismo. Fora isso peço mais nada.

Se houvesse erro em tempo de compilação, ficaria mais fácil explicar o que houve.

Listeiro 037 escreveu:

Perceba que o único modo de conseguir usar os ponteiros ou parâmetros do vetor argv[] é com o auxílio de argc, informado justamente para isto.


paulo1205 escreveu:

Talvez isso seja verdade em geral, mas acho interessante observar que não é verdade nos UNIXes em geral, e particularmente no Linux.

Um novo programa é sempre chamado pela system call execve(), que recebe, entre seus argumentos, um argv, do tipo char **, mas nada parecido com um inteiro argc. Essa função conta os argumentos de modo parecido com a forma como strings identificam o final da cadeia de caracteres: usando um marcador com valor nulo como último elemento do array. Esse marcador nulo, que deve estar presente quando execve() é invoca é passado por ela ao novo programa qur ela abre. Desse modo, é seguro fazer no Linux algo como vai abaixo.

int main(int argc, char **argv){
assert(argv[argc]==NULL);
/* ... */
}




Se o assunto é Unix-Like, melhor, Linux, não é uma verdade geral.

Só há um problema: estamos falando da função main() que usa uma variável que recebe um valor inteiro. Se vocẽ quiser usar essa execve que conta os parâmetros ao invés, aí é outra história ... :-P A exemplo do último parâmetro de main() pode ser acessada com uma função sem usar o parâmetro. Falei sobre isso lá atrás.

OBS: Faltou explicar a função assert() :-P

*** EDIT ***

etc.



8. Re: Diferença entre: char *argv[] e char argc[]

cr0n
_di0

(usa FreeBSD)

Enviado em 18/08/2012 - 00:24h

Listeiro 037 escreveu:

Quando você escreve char a[] = "abcd" é óbvio que você vê quatro caracteres após o sinal de igual. Mas e o tal barra-zero? É quatro ou é cinco? E se a string tivesse 1337 caracteres? E se fossem 1338? Nesse sentido desconhece-se quantos caracteres há na string. Não precisa contar, saber, pensar em quantos há exatamente. Inclusive char a[] = "abcd" é char *a = "abcd"



string literais são objetos imutáveis e nesses casos o compilador é esperto o suficiente para adicionar automaticamente null terminator.


const char *str = "foo";

seria algo como

const char *str = {'f', 'o', 'o', 0};


9. Re: Diferença entre: char *argv[] e char argc[]

Perfil removido
removido

(usa Nenhuma)

Enviado em 18/08/2012 - 00:47h

Aproveitando a ocasião, o que é esse foo-bar que todo mundo usa? É algum trocadilho? Não que seja importante, mas está em todo lugar e nunca me preocupei de saber o que é. Nem em dicionário devo ter procurado.

Mas voltando ao assunto, é exatamente isso, ou quase.

O que tentava dizer é quando se usa o par de colchetes vazios não se pensa no que vai na string, como ela funciona. O compilador é quem se vira. A notação facilita.

Eu não enxergo os colchetes exatamente como um ponteiro, embora possuam mecanismos semelhantes. Nem colchetes como se fosse um asterisco posfixo. Em princípio eu penso numa indeterminação. À rigor não quer dizer que o mecanismo interno de funcionamento seja este.

É apenas uma abstração pessoal. Ela melhora minha compreensão ao primeiro contato. Mas eu não conseguiria explicar além disso sem provocar um 'debate' à contra-gosto. Daí só resta me desculpar por não conseguir me expressar devidamente e por alguma incorreção de qualquer espécie.


10. Re: Diferença entre: char *argv[] e char argc[]

cr0n
_di0

(usa FreeBSD)

Enviado em 18/08/2012 - 00:53h

Saquei sua explicação, em relação ao foo, bar, etc...

http://pt.wikipedia.org/wiki/Foobar


11. Re: Diferença entre: char *argv[] e char argc[]

Perfil removido
removido

(usa Nenhuma)

Enviado em 18/08/2012 - 01:05h

_di0 escreveu:

Saquei sua explicação, em relação ao foo, bar, etc...

http://pt.wikipedia.org/wiki/Foobar


Boa! É uma incógnita computacional. :)

Eu li a página. Trollencio é ótima.


12. Re: Diferença entre: char *argv[] e char argc[]

Paulo
paulo1205

(usa Ubuntu)

Enviado em 20/08/2012 - 05:22h

Só há um problema: estamos falando da função main() que usa uma variável que recebe um valor inteiro. Se vocẽ quiser usar essa execve que conta os parâmetros ao invés, aí é outra história ... :-P A exemplo do último parâmetro de main() pode ser acessada com uma função sem usar o parâmetro. Falei sobre isso lá atrás.

OBS: Faltou explicar a função assert() :-P


Hein?

A chamada execve() não é uma alternativa a main(), mas sim a chamada que faz com que o kernel execute um programa novo.

Por exemplo, quando você digita no shell um comando como

expr 23 + 45 


o que o shell vai fazer é contar quantos são os componentes da linha de comando (4 strings, que no nosso exemplo são "expr", "23", "+" e "45"), alocar espaço na memória para 5 (i.e. 4+1) ponteiros de caracteres, aos quais ele vai atribuir os endereços das 4 strings e, no fim, um ponteiro nulo), descobrir um qual diretório do PATH reside o comando "expr", e executar esse programa. Em algum momento do tempo, ter-se-á executado alguma coisa funcionalmente equivalente a tudo o que vai abaixo.

extern char **environ; /* array com as variáveis de ambiente do processo */
char *executavel, **argumentos;
int nargs;

nargs=4;
argumentos=malloc((1+nargs)*sizeof(char *));
argumentos[0]="expr";
argumentos[1]="23";
argumentos[2]="+";
argumentos[3]="45";
argumentos[nargs]=NULL;
executavel="/usr/bin/expr";

execve(executavel, argumentos, environ);


Admitindo que o comando expr tenha sido escrito em C (e realmente foi!), sua função main() terá sido escrita na forma int main(int argc, char **argv). Quando ele for disparado pelo execve() mostrado acima, argc terá o valor 4, indicando que há em argv quatro elementos aproveitáveis (índices 0 a argc-1, que é 3, do array, portanto).

Eu digo, porém, que o tamanho real do array argv não é 4, mas 5, porque haverá (ao menos no Linux) um ponteiro nulo lodo após o último elemento "aproveitável". Esse ponteiro nulo poderia ser usado para descobrir o final da lista de argumentos sem consultar o valor de argc -- ao contrário do que você sugeriu ao dizer que usar argc é a única forma de percorrer argv.


Quanto a assert(), a manpage lhe dirá que é uma função que aborta o programa caso a condição testada seja falsa. Eu a usei no exemplo que mostrei só para deixar claro que o programa não será abortado se acaso resolver verificar se argv[argc] é realmente nulo ou não.



01 02



Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts